/*
** Xin YUAN, 2024, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////

#if defined(WIN32)

#include "windows/basic_types.h"

#elif defined(__linux__)

#include "linux/basic_types.h"

#else

#error This OS system is not supported!

#endif

//------------------------------------------------------------------------------
//functions

// right value

#ifndef rv_forward
template <typename T>
inline T&& rv_forward(T& t) noexcept
{
	return static_cast<T&&>(t);
}
#endif

// constructor

#pragma push_macro("new")
#undef new

#ifndef call_constructor
template <typename T, typename... Args>
inline void call_constructor(T& t, Args&&... args)
{
	::new(&t) T(rv_forward<Args>(args)...);
}
#endif

#pragma pop_macro("new")

// destructor

#ifndef call_destructor
template <typename T>
inline void call_destructor(T& t) noexcept
{
	t.~T();
}
#endif

// array constructors

#ifndef call_array_constructors
template <typename T, typename... Args>
inline void call_array_constructors(T* p, uintptr_t size, Args&&... args)
{
	uintptr_t i;
	try {
		for( i = 0; i < size; i ++ ) {
			call_constructor(*(p + i), rv_forward<Args>(args)...);
		}
	}
	catch(...) {
		while( i > 0 ) {
			i --;
			p[i].~T();
		}
		throw;  //re-throw
	}
}
#endif

// array destructors

#ifndef call_array_destructors
template <typename T>
inline void call_array_destructors(T* p, uintptr_t size) noexcept
{
	T* pT = p;
	for( uintptr_t i = size; i > 0; i -- ) {
		pT->~T();
		++ pT;
	}
}
#endif

//------------------------------------------------------------------------------
//classes

// exceptions

class ctl_exception
{
public:
	explicit ctl_exception(int32_t v = 0) noexcept : m_value(v)
	{
	}
	int32_t V() const noexcept
	{
		return m_value;
	}

protected:
	int32_t m_value;
};
class ctl_invalid_exception : public ctl_exception
{
public:
	ctl_invalid_exception() noexcept : ctl_exception(1)
	{
	}
};
class ctl_memory_exception : public ctl_exception
{
public:
	ctl_memory_exception() noexcept : ctl_exception(2)
	{
	}
};
class ctl_overflow_exception : public ctl_exception
{
public:
	ctl_overflow_exception() noexcept : ctl_exception(3)
	{
	}
};
class ctl_unexpected_exception : public ctl_exception
{
public:
	ctl_unexpected_exception() noexcept : ctl_exception(4)
	{
	}
};

// ctl_limits<T>

template <typename T>
class ctl_limits;

//special
template <>
class ctl_limits<int8_t>
{
public:
	static constexpr const int8_t Lowest = SCHAR_MIN;
	static constexpr const int8_t Min = SCHAR_MIN;
	static constexpr const int8_t Max = SCHAR_MAX;
};
template <>
class ctl_limits<uint8_t>
{
public:
	static constexpr const uint8_t Lowest = 0;
	static constexpr const uint8_t Min = 0;
	static constexpr const uint8_t Max = UCHAR_MAX;
};

template <>
class ctl_limits<int16_t>
{
public:
	static constexpr const int16_t Lowest = SHRT_MIN;
	static constexpr const int16_t Min = SHRT_MIN;
	static constexpr const int16_t Max = SHRT_MAX;
};
template <>
class ctl_limits<uint16_t>
{
public:
	static constexpr const uint16_t Lowest = 0;
	static constexpr const uint16_t Min = 0;
	static constexpr const uint16_t Max = USHRT_MAX;
};

template <>
class ctl_limits<int32_t>
{
public:
	static constexpr const int32_t Lowest = INT_MIN;
	static constexpr const int32_t Min = INT_MIN;
	static constexpr const int32_t Max = INT_MAX;
};
template <>
class ctl_limits<uint32_t>
{
public:
	static constexpr const uint32_t Lowest = 0;
	static constexpr const uint32_t Min = 0;
	static constexpr const uint32_t Max = UINT_MAX;
};

template <>
class ctl_limits<int64_t>
{
public:
	static constexpr const int64_t Lowest = LLONG_MIN;
	static constexpr const int64_t Min = LLONG_MIN;
	static constexpr const int64_t Max = LLONG_MAX;
};
template <>
class ctl_limits<uint64_t>
{
public:
	static constexpr const uint64_t Lowest = 0;
	static constexpr const uint64_t Min = 0;
	static constexpr const uint64_t Max = ULLONG_MAX;
};

template <>
class ctl_limits<float>
{
public:
	static constexpr const float Lowest = -FLT_MAX;
	static constexpr const float Min = FLT_MIN;
	static constexpr const float Max = FLT_MAX;
};
template <>
class ctl_limits<double>
{
public:
	static constexpr const double Lowest = -DBL_MAX;
	static constexpr const double Min = DBL_MIN;
	static constexpr const double Max = DBL_MAX;
};

// ctl_abs

inline int8_t ctl_abs(int8_t x) noexcept
{
	return (int8_t)::abs((int32_t)x);
}
inline int16_t ctl_abs(int16_t x) noexcept
{
	return (int16_t)::abs((int32_t)x);
}
inline int32_t ctl_abs(int32_t x) noexcept
{
	return ::abs(x);
}
inline int64_t ctl_abs(int64_t x) noexcept
{
	return ::llabs(x);
}
inline float ctl_abs(float x) noexcept
{
	return ::fabsf(x);
}
inline double ctl_abs(double x) noexcept
{
	return ::fabs(x);
}

// float

inline double ctl_acos(double x) noexcept
{
	return ::acos(x);
}
inline float ctl_acos(float x) noexcept
{
	return ::acosf(x);
}
inline double ctl_acosh(double x) noexcept
{
	return ::acosh(x);
}
inline float ctl_acosh(float x) noexcept
{
	return ::acoshf(x);
}
inline double ctl_asin(double x) noexcept
{
	return ::asin(x);
}
inline float ctl_asin(float x) noexcept
{
	return ::asinf(x);
}
inline double ctl_asinh(double x) noexcept
{
	return ::asinh(x);
}
inline float ctl_asinh(float x) noexcept
{
	return ::asinhf(x);
}
inline double ctl_atan2(double y, double x) noexcept
{
	return ::atan2(y, x);
}
inline float ctl_atan2(float y, float x) noexcept
{
	return ::atan2f(y, x);
}
inline double ctl_atanh(double x) noexcept
{
	return ::atanh(x);
}
inline float ctl_atanh(float x) noexcept
{
	return ::atanhf(x);
}
inline double ctl_cos(double x) noexcept
{
	return ::cos(x);
}
inline float ctl_cos(float x) noexcept
{
	return ::cosf(x);
}
inline double ctl_cosh(double x) noexcept
{
	return ::cosh(x);
}
inline float ctl_cosh(float x) noexcept
{
	return ::coshf(x);
}
inline double ctl_exp(double x) noexcept
{
	return ::exp(x);
}
inline float ctl_exp(float x) noexcept
{
	return ::expf(x);
}
inline double ctl_ldexp(double x, int exp) noexcept
{
	return ::ldexp(x, exp);
}
inline float ctl_ldexp(float x, int exp) noexcept
{
	return ::ldexpf(x, exp);
}
inline double ctl_log(double x) noexcept
{
	return ::log(x);
}
inline float ctl_log(float x) noexcept
{
	return ::logf(x);
}
inline double ctl_log10(double x) noexcept
{
	return ::log10(x);
}
inline float ctl_log10(float x) noexcept
{
	return ::log10f(x);
}
inline double ctl_log2(double x) noexcept
{
	return ::log2(x);
}
inline float ctl_log2(float x) noexcept
{
	return ::log2f(x);
}
inline double ctl_pow(double x, double y) noexcept
{
	return ::pow(x, y);
}
inline float ctl_pow(float x, float y) noexcept
{
	return ::powf(x, y);
}
inline double ctl_sin(double x) noexcept
{
	return ::sin(x);
}
inline float ctl_sin(float x) noexcept
{
	return ::sinf(x);
}
inline double ctl_sinh(double x) noexcept
{
	return ::sinh(x);
}
inline float ctl_sinh(float x) noexcept
{
	return ::sinhf(x);
}
inline double ctl_sqrt(double x) noexcept
{
	return ::sqrt(x);
}
inline float ctl_sqrt(float x) noexcept
{
	return ::sqrtf(x);
}
inline double ctl_tan(double x) noexcept
{
	return ::tan(x);
}
inline float ctl_tan(float x) noexcept
{
	return ::tanf(x);
}
inline double ctl_tanh(double x) noexcept
{
	return ::tanh(x);
}
inline float ctl_tanh(float x) noexcept
{
	return ::tanhf(x);
}

inline float ctl_ceil(float x) noexcept
{
	return ::ceilf(x);
}
inline double ctl_ceil(double x) noexcept
{
	return ::ceil(x);
}
inline float ctl_floor(float x) noexcept
{
	return ::floorf(x);
}
inline double ctl_floor(double x) noexcept
{
	return ::floor(x);
}

inline float ctl_round(float x) noexcept
{
	return ::roundf(x);
}
inline double ctl_round(double x) noexcept
{
	return ::round(x);
}
inline float ctl_trunc(float x) noexcept
{
	return ::truncf(x);
}
inline double ctl_trunc(double x) noexcept
{
	return ::trunc(x);
}

// byte order

template <typename T1, typename T2>
struct ctl_union_pair
{
	union { T1 v1; T2 v2; };
};

inline uint16_t ctl_byte_order_swap(uint16_t x) noexcept
{
	return ((x & 0x00FF) << 8) | ((x >> 8) & 0x00FF);
}
inline uint32_t ctl_byte_order_swap(uint32_t x) noexcept
{
	return ((x & 0x000000FF) << 24)
		| ((x & 0x0000FF00) << 8)
		| ((x & 0x00FF0000) >> 8)
		| ((x & 0xFF000000) >> 24);
}
inline uint64_t ctl_byte_order_swap(uint64_t x) noexcept
{
	return ((x & (uint64_t)0x00FF) << 56)
		| ((x & (((uint64_t)0x00FF) << 8)) << 40)
		| ((x & (((uint64_t)0x00FF) << 16)) << 24)
		| ((x & (((uint64_t)0x00FF) << 24)) << 8)
		| ((x & (((uint64_t)0x00FF) << 32)) >> 8)
		| ((x & (((uint64_t)0x00FF) << 40)) >> 24)
		| ((x & (((uint64_t)0x00FF) << 48)) >> 40)
		| ((x & (((uint64_t)0x00FF) << 56)) >> 56);
}
inline int16_t ctl_byte_order_swap(int16_t x) noexcept
{
	return (int16_t)ctl_byte_order_swap((uint16_t)x);
}
inline int32_t ctl_byte_order_swap(int32_t x) noexcept
{
	return (int32_t)ctl_byte_order_swap((uint32_t)x);
}
inline int64_t ctl_byte_order_swap(int64_t x) noexcept
{
	return (int64_t)ctl_byte_order_swap((uint64_t)x);
}
inline float ctl_byte_order_swap(float x) noexcept
{
	ctl_union_pair<float, uint32_t> up{x};
	up.v2 = ctl_byte_order_swap(up.v2);
	return up.v1;
}
inline double ctl_byte_order_swap(double x) noexcept
{
	ctl_union_pair<double, uint64_t> up{x};
	up.v2 = ctl_byte_order_swap(up.v2);
	return up.v1;
}

// character

inline bool ctl_is_alnum(char ch) noexcept
{
	return ::isalnum((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_alnum(wchar_t ch) noexcept
{
	return ::iswalnum((wint_t)ch) != 0;
}
inline bool ctl_is_alpha(char ch) noexcept
{
	return ::isalpha((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_alpha(wchar_t ch) noexcept
{
	return ::iswalpha((wint_t)ch) != 0;
}
inline bool ctl_is_cntrl(char ch) noexcept
{
	return ::iscntrl((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_cntrl(wchar_t ch) noexcept
{
	return ::iswcntrl((wint_t)ch) != 0;
}
inline bool ctl_is_graph(char ch) noexcept
{
	return ::isgraph((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_graph(wchar_t ch) noexcept
{
	return ::iswgraph((wint_t)ch) != 0;
}
inline bool ctl_is_lower(char ch) noexcept
{
	return ::islower((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_lower(wchar_t ch) noexcept
{
	return ::iswlower((wint_t)ch) != 0;
}
inline bool ctl_is_upper(char ch) noexcept
{
	return ::isupper((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_upper(wchar_t ch) noexcept
{
	return ::iswupper((wint_t)ch) != 0;
}
inline bool ctl_is_digit(char ch) noexcept
{
	return ::isdigit((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_digit(wchar_t ch) noexcept
{
	return ::iswdigit((wint_t)ch) != 0;
}
inline bool ctl_is_xdigit(char ch) noexcept
{
	return ::isxdigit((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_xdigit(wchar_t ch) noexcept
{
	return ::iswxdigit((wint_t)ch) != 0;
}
inline bool ctl_is_blank(char ch) noexcept
{
	return ::isblank((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_blank(wchar_t ch) noexcept
{
	return ::iswblank((wint_t)ch) != 0;
}
inline bool ctl_is_space(char ch) noexcept
{
	return ::isspace((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_space(wchar_t ch) noexcept
{
	return ::iswspace((wint_t)ch) != 0;
}
inline bool ctl_is_print(char ch) noexcept
{
	return ::isprint((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_print(wchar_t ch) noexcept
{
	return ::iswprint((wint_t)ch) != 0;
}
inline bool ctl_is_punct(char ch) noexcept
{
	return ::ispunct((int)(unsigned char)ch) != 0;
}
inline bool ctl_is_punct(wchar_t ch) noexcept
{
	return ::iswpunct((wint_t)ch) != 0;
}

inline char ctl_to_lower(char ch) noexcept
{
	return (char)::tolower((int)(unsigned char)ch);
}
inline wchar_t ctl_to_lower(wchar_t ch) noexcept
{
	return (wchar_t)::towlower((wint_t)ch);
}
inline char ctl_to_upper(char ch) noexcept
{
	return (char)::toupper((int)(unsigned char)ch);
}
inline wchar_t ctl_to_upper(wchar_t ch) noexcept
{
	return (wchar_t)::towupper((wint_t)ch);
}

inline uintptr_t ctl_calc_length(const char* sz) noexcept
{
	return ::strlen(sz);
}
inline uintptr_t ctl_calc_length(const wchar_t* wsz) noexcept
{
	return ::wcslen(wsz);
}

// compare trait

template <typename T>
class ctl_compare_trait_base
{
public:
	//common versions
	static bool IsEQ(const T& t1, const T& t2) noexcept
	{
		return t1 == t2;
	}
	static bool IsNE(const T& t1, const T& t2) noexcept
	{
		return t1 != t2;
	}
	static bool IsGT(const T& t1, const T& t2) noexcept
	{
		return t1 > t2;
	}
	static bool IsLT(const T& t1, const T& t2) noexcept
	{
		return t1 < t2;
	}
	static bool IsGE(const T& t1, const T& t2) noexcept
	{
		return t1 >= t2;
	}
	static bool IsLE(const T& t1, const T& t2) noexcept
	{
		return t1 <= t2;
	}
	static int Compare(const T& t1, const T& t2) noexcept
	{
		return IsLT(t1, t2) ? -1 : (IsEQ(t1, t2) ? 0 : (assert( IsGT(t1, t2) ), 1));
	}
};

template <typename T>
class ctl_default_compare_trait : public ctl_compare_trait_base<T>
{
};

//special versions
#define _CTL_DECLARE_INT_COMPARE_TRAIT(T)  \
	template <> class ctl_default_compare_trait<T> : public ctl_compare_trait_base<T> {  \
	public:  \
	static int Compare(const T& t1, const T& t2) noexcept { return (int)t1 - (int)t2; }  \
	};

// sizeof(T) must be less than sizeof(int) or sizeof(unsigned int)
_CTL_DECLARE_INT_COMPARE_TRAIT(int8_t)
_CTL_DECLARE_INT_COMPARE_TRAIT(uint8_t)
_CTL_DECLARE_INT_COMPARE_TRAIT(int16_t)
_CTL_DECLARE_INT_COMPARE_TRAIT(uint16_t)
_CTL_DECLARE_INT_COMPARE_TRAIT(bool)

template <>
class ctl_default_compare_trait<float>
{
public:
	static bool IsEQ(const float& t1, const float& t2) noexcept
	{
		return ::fabsf(t1 - t2) < FLT_EPSILON;
	}
	static bool IsNE(const float& t1, const float& t2) noexcept
	{
		return ::fabsf(t1 - t2) >= FLT_EPSILON;
	}
	static bool IsGT(const float& t1, const float& t2) noexcept
	{
		return IsLT(t2, t1);
	}
	static bool IsLT(const float& t1, const float& t2) noexcept
	{
		return t1 + FLT_EPSILON <= t2;
	}
	static bool IsGE(const float& t1, const float& t2) noexcept
	{
		return !IsLT(t1, t2);
	}
	static bool IsLE(const float& t1, const float& t2) noexcept
	{
		return !IsGT(t1, t2);
	}
	static int Compare(const float& t1, const float& t2) noexcept
	{
		return IsLT(t1, t2) ? -1 : IsGT(t1, t2) ? 1 : 0;
	}
};

template <>
class ctl_default_compare_trait<double>
{
public:
	static bool IsEQ(const double& t1, const double& t2) noexcept
	{
		return ::fabs(t1 - t2) < DBL_EPSILON;
	}
	static bool IsNE(const double& t1, const double& t2) noexcept
	{
		return ::fabs(t1 - t2) >= DBL_EPSILON;
	}
	static bool IsGT(const double& t1, const double& t2) noexcept
	{
		return IsLT(t2, t1);
	}
	static bool IsLT(const double& t1, const double& t2) noexcept
	{
		return t1 + DBL_EPSILON <= t2;
	}
	static bool IsGE(const double& t1, const double& t2) noexcept
	{
		return !IsLT(t1, t2);
	}
	static bool IsLE(const double& t1, const double& t2) noexcept
	{
		return !IsGT(t1, t2);
	}
	static int Compare(const double& t1, const double& t2) noexcept
	{
		return IsLT(t1, t2) ? -1 : IsGT(t1, t2) ? 1 : 0;
	}
};

////////////////////////////////////////////////////////////////////////////////
